<template>
  <n-spin class="dialogue-container h-full" :show="mainStore.pageLoading">
    <n-scrollbar ref="msgScrollbarRef" trigger="none" class="msg-content">
      <div v-if="chatStore.isNewChat && $route.params.id === 'new'" class="content-new">
        <div class="role-list-box xs:mt-11 m:mt-[18%] xl:mt-[25%]">
          <n-h1 class="content-title xs:mb-8 m:mb-12 xs:text-2xl m:text-3xl">
            {{ mainStore.pageTitle }}
          </n-h1>
          <n-grid x-gap="10" y-gap="25" cols="xs:3 s:3" responsive="screen">
            <!-- <n-gi v-for="item in chatStore.gridData" :key="item.title" class="text-center">
              <n-text class="text-lg">{{ item.title }}</n-text>
              <n-space class="mt-4" vertical :size="15">
                <div
                  v-for="text in item.data"
                  :key="text"
                  :class="[
                    'rounded',
                    'p-3',
                    'bg-slate-600/10',
                    'dark:bg-[#3E3F4B]',
                    { 'cursor-pointer': item.isLink },
                    { 'hover:bg-[#D9D9E3]': item.isLink },
                    { 'dark:hover:bg-[#202123]': item.isLink },
                  ]"
                  @click="item.isLink ? chatStore.changeMsg(text) : ''"
                >
                  {{ text }}
                </div>
              </n-space>
            </n-gi> -->
            <n-gi v-for="(item, index) in chatStore.roleList" :key="item.role" class="flex justify-center items-center">
              <div
                :class="{
                  'bg-slate-300': chatStore.selectRole === item.role,
                  'dark:bg-slate-500': chatStore.selectRole === item.role,
                }"
                class="role-item hover:bg-slate-300 dark:hover:bg-slate-500"
                @click="chatStore.changeRole(item.role, index)"
              >
                <div class="w-20">
                  <img :src="getImageUrl('chat', item.role)" :alt="item.role" />
                </div>
                <span class="dark:text-gray-200" :class="{ 'dark:text-gary-500': chatStore.selectRole === item.role }">
                  {{ item.name }}
                </span>
              </div>
            </n-gi>
          </n-grid>
        </div>
      </div>
      <div v-for="item in chatStore.content" v-else :key="item" class="msg-box">
        <n-thing
          content-indented
          class="overflow-x-auto xs:gap-2 m:gap-3"
          :class="{ 'justify-end': item.role === 'user', 'flex-row-reverse': item.role === 'user' }"
        >
          <template #avatar>
            <div v-if="item.role === 'user'">
              <div v-if="userStore.head_img" class="head-img xs:w-10 xs:h-10 m:w-12 m:h-12">
                <img class="rounded-full" :src="userStore.head_img" alt="person" />
              </div>
              <div v-else class="head-img xs:w-10 xs:h-10 m:w-12 m:h-12 p-2 bg-slate-300 dark:bg-slate-500">
                <img :src="getImageUrl('chat', 'person')" alt="person" />
              </div>
            </div>
            <div v-else class="head-img xs:w-10 xs:h-10 m:w-12 m:h-12 p-2 bg-slate-300 dark:bg-slate-500">
              <img :src="getImageUrl('chat', chatStore.roleList[msgState.msgParams.chat_role]?.role)" alt="robot" />
            </div>
          </template>
          <div class="message flex flex-col h-full" :class="{ 'items-end': item.role === 'user' }">
            <n-text
              class="text-[#999] inline-flex items-center xs:gap-2 m:gap-3 text-sm mb-2"
              :class="{ 'text-right': item.role === 'user' }"
            >
              <span v-if="item.role !== 'user'">
                {{ chatStore.roleList[msgState.msgParams.chat_role]?.name }}
              </span>
              <span>{{ timeFormat(item.sendTime) }}</span>
            </n-text>
            <div
              class="message-box flex h-full xs:gap-2 m:gap-3 items-end"
              :class="{ 'flex-row-reverse': item.role === 'user' }"
            >
              <n-scrollbar
                v-if="item.role === 'user'"
                x-scrollable
                trigger="none"
                class="dark:bg-slate-500/20 bg-slate-600/10 px-4 max-w-full w-fit"
              >
                <div class="msg-line">{{ item.content }}</div>
              </n-scrollbar>
              <div v-else class="rebot-message dark:bg-slate-500/20 bg-slate-600/10 px-4">
                <span v-if="item.content === ''" class="cursor bg-[#374151] dark:bg-[#D1D5DB]"></span>
                <VMdPreview :text="item.content" @copy-code-success="handleCopyCodeSuccess" />
                <span v-if="msgState.msgError">
                  <n-button
                    v-if="item.content.indexOf('请登录后进行使用') != -1"
                    text
                    size="large"
                    type="success"
                    @click="emit('openWechatLogin')"
                  >
                    立即登录
                  </n-button>
                  <span v-if="item.content.indexOf('每日免费次数3次已用完') != -1">
                    您可以使用
                    <n-button size="large" text type="success" @click="$router.push('/plan')">套餐订阅</n-button>
                    <!-- 或
                    <n-button size="large" text type="success" @click="$router.push('/invite')">
                      邀请好友
                    </n-button> -->
                    获得更多的对话次数。
                  </span>
                </span>
              </div>
              <n-button
                v-if="item.content"
                :focusable="false"
                tertiary
                circle
                size="large"
                @click="copyText(item.content)"
              >
                <template #icon>
                  <n-icon :component="CopyOutline" />
                </template>
              </n-button>
            </div>
          </div>
        </n-thing>
      </div>
    </n-scrollbar>
    <div class="chat-input-box p-4 flex gap-4 pb-8 border-t dark:border-t-white/20">
      <!-- <n-button @mousedown="startSpeech" @mouseup="stopSpeech" @touchstart="startSpeech" @touchend="stopSpeech">
        {{ isPlaying ? '停止' : '播放' }}
      </n-button> -->
      <!-- <n-button @mousedown="startRecognition" @mouseup="stopRecognition" @touchstart="startRecognition"
        @touchend="stopRecognition">
        <template #icon>
          <n-icon :component="MicOffOutline" size="26" />
        </template>
      </n-button> -->
      <n-input
        ref="chatInputRef"
        v-model:value="msgState.msgParams.message"
        size="large"
        clearable
        type="textarea"
        :autosize="{ minRows: 1, maxRows: 6 }"
        show-count
        maxlength="1500"
        :placeholder="`请输入消息内容${!isMobile() && !isWeiXin() ? '（按住shift+enter可换行）' : ''}`"
        rows="1"
        class="shadow-[0_0_10px_rgba(0,0,0,0.10)] max-w-[930px] bg-slate-100 dark:bg-[#494a54]"
        @focus="scrollToBottom"
        @keydown.enter="sendMsg"
      />
      <n-button
        class="send-msg-btn"
        type="primary"
        :disabled="!msgState.msgParams.message || chatStore.getting"
        @click="sendMsg"
      >
        <template #icon>
          <n-icon :component="SendOutline" />
        </template>
      </n-button>
    </div>
  </n-spin>
</template>

<script setup>
import { reactive, getCurrentInstance, nextTick, inject } from 'vue'
import { useUserStore, useMainStore, useChatStore } from '@/store'
import { useRoute, onBeforeRouteUpdate } from 'vue-router'
import { messageContents } from '@api/chatApi'
import { SendOutline, CopyOutline, MicOffOutline, MicOutline } from '@vicons/ionicons5'
import { EventSourcePolyfill } from 'event-source-polyfill'
import { baseURL } from '@/axios'
import { copyText, getImageUrl, timeFormat, isMobile, isWeiXin } from '@/utils'
import VMdPreview from '@/plugins/vmdPreview'
import { NButton } from 'naive-ui'
import { useSpeechRecognition, useEventSource } from '@vueuse/core'

// 判断是否为开发环境
const isDev = inject('isDev')
const stream = () => (isDev ? import('@/utils/stream.dev.js') : import('@/utils/stream.prod.js'))

const userStore = useUserStore()
const mainStore = useMainStore()
const chatStore = useChatStore()

const emit = defineEmits(['openWechatLogin'])

const { proxy } = getCurrentInstance()

const route = useRoute()

const msgState = reactive({
  msgError: false,
  msgParams: {
    chat_role: 0,
    message: chatStore.message || '',
  },
})

// const isPlaying = ref(false)
// const speechText = '这里是要转换为语音的文本'
// const utterance = new SpeechSynthesisUtterance(speechText)

// const startSpeech = () => {
//   if (!isPlaying.value) {
//     window.speechSynthesis.speak(utterance)
//     isPlaying.value = true;
//   }
// }

// const stopSpeech = () => {
//   if (isPlaying.value) {
//     window.speechSynthesis.cancel()
//     isPlaying.value = false;
//   }
// }

// const { isSupported, listen, stop } = useSpeechRecognition({
//   lang: 'zh-CN',
//   continuous: true,
//   interimResults: true,
// })

// const startRecognition = () => {
//   console.log(isSupported);
//   if (isSupported) {
//     listen((text) => {
//       console.log(text);
//       msgState.msgParams.message = text;
//     })
//   } else {
//     $message.error('您的浏览器暂不支持录音功能。', { duration: 2000 })
//   }
// }

// const stopRecognition = () => {
//   stop()
// }

const scrollToBottom = () => {
  nextTick(() => {
    const msgScrollbar = proxy.$refs.msgScrollbarRef
    const scrollHeight = proxy.$refs.msgScrollbarRef?.$el.nextElementSibling.firstChild.scrollHeight
    msgScrollbar && msgScrollbar.scrollBy(0, scrollHeight)
  })
}

const getMsgContent = async id => {
  if (id !== 'new') {
    try {
      mainStore.setPageLoading(true)
      const res = await messageContents({ conversationId: id })
      if (res.statusCode === 200) {
        chatStore.content = res.data
        chatStore.selectRoleKey = res.chat_role
        chatStore.conversationId = res.conversation_id
        msgState.msgParams.chat_role = res.chat_role
        chatStore.isAddedChat = res.conversation_id ? true : false
        chatStore.setDocTitle(res.title)
        scrollToBottom()
      } else {
        chatStore.openNewChat()
      }
    } catch {
      chatStore.openNewChat()
    } finally {
      mainStore.setPageLoading(false)
    }
  }
}

if (route.params.id === 'new') {
  chatStore.isNewChat = true
  chatStore.setDocTitle('新对话窗口')
  chatStore.conversationId = ''
} else {
  getMsgContent(route.params.id)
}

onBeforeRouteUpdate(({ params }) => {
  getMsgContent(params.id)
})

const sendMsg = async event => {
  console.log(event)
  if (!event.shiftKey) {
    event.preventDefault()
    if (msgState.msgParams.message?.trim() && !chatStore.getting) {
      chatStore.isNewChat = false
      msgState.msgError = false
      chatStore.getting = true
      msgState.msgParams.chat_role = chatStore.selectRoleKey
      chatStore.content.push(
        ...[
          {
            content: msgState.msgParams.message,
            role: 'user',
            sendTime: +new Date(),
          },
          {
            content: '',
            role: 'assistant',
            sendTime: +new Date(),
          },
        ]
      )
      let index = chatStore.content.length - 1
      const { message, chat_role } = { ...msgState.msgParams }
      msgState.msgParams.message = ''
      scrollToBottom()
      const url = `${baseURL}/message/accomplish`

      const _params = JSON.stringify({
        message: encodeURIComponent(message),
        conversationId: chatStore.conversationId === 'new' ? '' : chatStore.conversationId,
        chat_role,
      })

      const { secret } = await stream()

      const eventStream = new EventSourcePolyfill(url, {
        headers: { ...secret(_params) },
      })
      // const { status, data, error, close } = useEventSource(url);

      eventStream.onmessage = function (event) {
        if (event.data === 'done') {
          chatStore.getting = false
          return eventStream.close()
        }
        const { conversationId, data } = JSON.parse(event.data)
        const result = JSON.parse(data)
        if (chatStore.content.length <= 2 && !chatStore.isAddedChat && userStore.isLogin) {
          chatStore.addChat({ title: message, conversation_id: conversationId })
          chatStore.setDocTitle(message)
          chatStore.conversationId = conversationId
          chatStore.isAddedChat = true
        }
        chatStore.content[index].id = result.id
        result.choices[0]?.delta?.content && (chatStore.content[index].content += result.choices[0].delta.content)
        scrollToBottom()
      }

      eventStream.onerror = function (error) {
        msgState.msgError = true
        eventStream.close()
        chatStore.getting = false
        scrollToBottom()
        const errorText = '页面过期，请刷新后重试。'
        if (error?.data?.indexOf('非法请求') != -1) {
          return (chatStore.content[index].content = errorText)
        }
        chatStore.content[index].content = error?.data || errorText
      }
    }
  }
}

const handleCopyCodeSuccess = () => {
  $message.success('复制成功', { duration: 1200 })
}
</script>

<style lang="less" scoped>
.dialogue-container {
  width: 100%;

  &:deep(.n-spin-content) {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;

    .content-new {
      max-width: 848px;
      margin: 0 auto;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      .role-list-box {
        width: 100%;
        height: 100%;
        overflow: auto;
        display: flex;
        align-items: center;
        flex-direction: column;
        .content-title {
          text-align: center;
          font-weight: 600;
        }
        .n-grid {
          .role-item {
            width: 128px;
            height: 128px;
            display: inline-flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            gap: 12px;
            padding: 12px;
            border-radius: 12px;
            cursor: pointer;
            transition: all 0.2s ease-out;
          }
        }
      }
    }

    .msg-content {
      width: 100%;
      overflow: auto;
      padding: 0 14px;
      .msg-box {
        width: 100%;
        min-height: 85px;
        padding-bottom: 20px;
        display: flex;
        align-items: center;
        justify-content: center;

        &:nth-of-type(1) {
          padding-top: 20px;
        }

        .flex-row-reverse .n-thing-main__content {
          flex-direction: row-reverse;
        }

        .flex-row-reverse .n-thing-avatar {
          margin-right: 0;
          margin-left: 12px;
        }

        .n-thing {
          width: 1000px;
          font-size: 16px;
          .n-thing-avatar {
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            align-items: center;
            margin: 0;
            .head-img {
              width: 48px;
              height: 48px;
              display: flex;
              justify-content: center;
              align-items: center;
              border-radius: 100%;
              flex-shrink: 0;
            }
          }

          .n-thing-main {
            width: 100%;
            overflow: hidden;

            .n-thing-main__content {
              display: flex;
              align-items: center;
              height: 100%;
              width: 100%;

              .message {
                max-width: 100%;
                .message-box {
                  max-width: 100%;
                  > div {
                    padding: 12px;
                    border-radius: 10px;
                    width: fit-content;
                    height: auto;
                  }
                  .rebot-message {
                    width: fit-content;
                    max-width: 100%;
                    .v-md-pre-wrapper.copy-code-mode {
                      width: 100%;
                    }

                    .cursor {
                      width: 4px;
                      height: 100%;
                      display: inline-block;
                      animation: twinkle 1s infinite cubic-bezier(0.97, -0.4, 0, 1.32);
                    }
                  }
                }

                @keyframes twinkle {
                  from {
                    opacity: 1;
                  }

                  to {
                    opacity: 0;
                  }
                }
              }
            }
          }
        }

        .msg-line {
          width: fit-content;
          white-space: pre-line;
        }
      }
    }

    .chat-input-box {
      width: 100%;
      display: flex;
      justify-content: center;
      align-items: flex-end;
      background: transparent;

      .n-input {
        min-height: 48px;
        display: flex;
        align-items: center;
        justify-content: center;

        .n-input-wrapper {
          .n-input__textarea {
            .n-input__textarea-el {
              margin-top: 1px;
              caret-color: unset;
            }

            .n-input__placeholder {
              top: 2px !important;
            }
          }
        }
      }

      .n-button {
        height: 48px;
      }

      .send-msg-btn {
        .n-icon {
          transform: rotate(-35deg);
          margin-left: 3px;
          margin-top: -2px;
        }
      }
    }
  }
}
</style>
